const path = require('path');
const os = require('os');
const {app, shell, Menu, Tray, Notification} = require('electron');
const CtlUtil = require("./utils/CtlUtil");
require('./format/StrFormat');
const appPath = app.getAppPath();
// os.tmpdir = ()=>{
//     return "{}/pm2_home".format(appPath);
// };

// process.env.OVER_HOME = "./pm2_home";
const pm2 = require("pm2");
const ConfigLoader = require('./config/ConfigLoader')
const elog = require('./log/elog');
const WaitForIt = require('./waitfor/WaitForIt');
const TrayMenuBuilder = require('./menu/TrayMenuBuilder');
const ServiceStates = require("./consts/ServiceStates");
global.appPath = appPath;

const waitForIt = new WaitForIt();

// 设置日志的输出目录
elog.transports.file.resolvePath = () => path.join(appPath, '/logs/sys/sys.log');
const ICON_PATH = path.join(appPath, '/public/icon');
const SERVICES_PATH = path.join(appPath, '/resources/services');
const MONGO_DB_PATH = path.join(appPath, '/resources/mongodb');
const ICON = path.join(ICON_PATH, 'appIcon.png');
let configLoader = new ConfigLoader(appPath);
let trayMenuBuilder = new TrayMenuBuilder(appPath);

const STATE_ONLINE = ServiceStates.online.EN;
const STATE_NOT_READY = ServiceStates.notReady.EN;
const STATE_ERRORED = ServiceStates.errored.EN;
const LOCAL_HOST = "localhost";
const MAX_STATE_CHECK_TIME = 10;
const STATE_CHECK_TIME_INTERVAL = 5 * 1000;

let tray;
let trayContextMenu;
let basicMenu;
//pm2 install pm2-logrotate-ext https://github.com/Lujo5/pm2-logrotate-ext
//pm2 install pm2-webshell https://github.com/pm2-hive/pm2-webshell
// 设置开机自动启动，在程序运行后会将配置写到注册表中，注意在调试环境运行和打包后运行写入注册表的启动路径不同，
// 在cmd中运行regedit命令打开注册表，在地址栏输入"计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"，
// 可以看到开机启动项的配置，删除调试环境运行时加入的配置，否则开机重启后除了会启动本程序，还会启动electron的示例程序

init();

function init() {
    app.setLoginItemSettings({
        openAtLogin: true,
        openAsHidden: false,
        path: process.execPath
    });
    process.on("SIGINT", onAppQuit)
    process.on('SIGTERM', onAppQuit);

    const gotTheLock = app.requestSingleInstanceLock()
    if (!gotTheLock) {
        app.quit()
    } else {
        app.on('second-instance', (event) => {
            if (tray) {
                tray.focus();
                notification("控制台已运行");
            }
        });
        app.on('ready', () => {
            //pm2 配置加载完成
            configLoader.init(inited => {
                if (!inited) {
                    elog.error("pm2配置初始化失败");
                    process.exit(1);
                }
                onAppReady();
            });
        })
    }
}

function onAppReady() {
    doConnectPm2Daemon();
    // 创建一个带图标的托盘应用
    tray = new Tray(ICON);
    tray.setToolTip('服务控制台');
    tray.closeContextMenu();
    basicMenu = trayMenuBuilder.newMenu(configLoader.serviceWrapperMap, onServiceItemClick,
        pm2RestartAllAppSvc, pm2DeleteAllServices, onAppQuit);
    trayContextMenu = Menu.buildFromTemplate(basicMenu);
    tray.setContextMenu(trayContextMenu);
}

function doConnectPm2Daemon() {
    pm2.connect(true, (err, meta) => {
        if (err) elog.error(err);
        console.log(meta);
        if (!meta.new_pm2_instance) {//create pm2 daemon by electron
            elog.log("连接到已存在的 pm2 服务进程");
        } else {
            elog.log("创建 pm2 服务进程");
        }
        //load log monitor
        require("./log/logMonitor");
        onPm2DaemonConnectSuccess();
    });
}

function onServiceItemClick(serviceName) {
    // TODO: on service item click on
}

/**
 * pm2 daemon connect success
 */
function onPm2DaemonConnectSuccess() {
    pm2RestartAllAppSvc();
}

function onAppQuit() {
    pm2.killDaemon(err => {
        elog.info("kill pm2 process");
        app.quit();
    });

}

/**
 * start all services by depends on
 */
function pm2StartAllServicesByDepends() {
    elog.log("开始启动所有服务 ..........")
    resetServiceWrappersState();
    waitForIt.doServiceLaunch(configLoader.serviceWrapperMap, startServiceByEntity);
}

function doAllServiceCheckedNotification(msg) {
    let size = configLoader.serviceWrapperMap.size;
    let count = 0;
    for (let [serviceName, serviceWrapper] of configLoader.serviceWrapperMap) {
        serviceWrapper.serviceEntity.checked && count++;
    }
    if (size === count) {
        notification(msg);
    }
}

/**
 * service state change callback
 * @param serviceEntity serviceEntity
 * @param state state 'online' 'error'
 */
function refreshTrayListItemView(serviceEntity, state) {
    let serviceName = serviceEntity.name;
    elog.info("接收服务[{}]状态变更[{}],刷新托盘".format(serviceName, state));
    let subItems = basicMenu[0].submenu;
    for (let subItem of subItems) {
        //[state] serviceName
        if (subItem.id && subItem.id === serviceName) {
            subItem.label = trayMenuBuilder.formatLabel(serviceName, serviceEntity.version, state);
        }
    }
    //set title
    let title = trayMenuBuilder.formatTitle(configLoader.serviceWrapperMap);
    subItems[0].label = title;
    trayContextMenu = Menu.buildFromTemplate(basicMenu);
    tray.setContextMenu(trayContextMenu);
    //notification
    doAllServiceCheckedNotification(title);
}

/**
 * 如果有在运行的服务则停止所有服务,没有则什么都不做
 * tip：不能删除 pm2扩展模块服务
 */
async function pm2DeleteAllServices() {
    elog.log("开始删除所有服务...");
    let procList = await listPm2ProcNoModules(true);
    if (procList.length === 0) return;
    for (let proc of procList) {
        elog.log("开始删除服务 [{}]->[{}]".format(proc.name, proc.pid));
        let serviceEntity = configLoader.getServiceEntityByName(proc.name);
        serviceEntity && (serviceEntity.state = ServiceStates.stopped.EN && refreshTrayListItemView(serviceEntity, ServiceStates.stopping.EN));
        pm2.delete(proc.name, error => {
            if (error != null) {
                elog.error("pm2 删除服务异常:[{}] pid:[{}] error:{}".format(proc.name, proc.pid, error.message));
                return;
            }
            elog.log("pm2 成功删除服务:[{}]".format(proc.name));
            serviceEntity && refreshTrayListItemView(serviceEntity, ServiceStates.stopped.EN);
        })
    }
}

/**
 * restart所有服务
 */
async function pm2RestartAllAppSvc() {
    let procList = await listPm2ProcNoModules(true);
    if (procList.length === 0) {
        pm2StartAllServicesByDepends();
        return;
    }
    //如果已存在app服务，先删除，然后再执行startAll
    let procDelCount = 0;
    for (let proc of procList) {
        pm2.delete(proc.name, error => {
            procDelCount++;
            if (error != null) {
                elog.error("pm2 删除服务异常[{}] error:{}".format(proc.name, error.message));
            } else {
                elog.log("pm2 成功删除服务:[{}] pid:[{}]".format(proc.name, proc.pid))
            }
            if (procDelCount === procList.length) {
                pm2StartAllServicesByDepends();
            }
        });
    }
}

function listPm2ProcNoModules(excludePmxModule) {
    return new Promise((resolve, reject) => {
        pm2.list((error, processList) => {
            if (error) {
                elog.error("pm2 list 错误:" + error.message)
                reject(error);
                return;
            }
            let moduleProcessList = new Array();
            if (processList.length !== 0) {
                for (let proc of processList) {
                    if (proc.pm2_env.pmx_module) {//模块服务
                        if (excludePmxModule) {
                            let index = processList.indexOf(proc);
                            processList.splice(index, 1);
                        }
                        moduleProcessList.push(proc.name);
                    }
                }
            }
            processList = processList ? processList : Array.of();
            let svcSize = excludePmxModule ? processList.length : processList.length - moduleProcessList.length;
            elog.log("[pm2 list] 找到 [{}] 应用服务,[{}] 模块服务 [{}]".format(svcSize, moduleProcessList.length, moduleProcessList))
            resolve(processList);
        })
    });
}

async function startServiceByEntity(serviceWrapper) {
    let serviceEntity = serviceWrapper.serviceEntity;
    let success = false;
    if (serviceEntity.enableCheck === true && serviceEntity.enablePm2) {//只是检查的服务
        success = true;
    } else {
        let pm2Config = configLoader.loadAServicePm2ConfigByEntity(serviceEntity);
        //TODO:检查端口是否已经被占用？
        let host = serviceEntity.host;
        host = host ? host : LOCAL_HOST;
        let port = serviceEntity.port;
        let readyUsed = await waitForIt.checkServiceOnline(host, port);
        if (readyUsed) {
            elog.error("端口[{}]已经占用，无法启动服务[{}]".format(port, serviceEntity.name));
            success = false;
        } else {
            success = await pm2StartServiceByConfig(pm2Config);
        }
    }
    if (success) {
        //check service if real ready
        //pm2 启动失败的服务 pid=0 state=errored
        let count = 0, timer = null;

        function loopCheck() {
            timer = setTimeout(async () => {
                let host = serviceEntity.host;
                host = host ? host : LOCAL_HOST;
                let port = serviceEntity.port;
                let ready = await waitForIt.checkServiceOnline(host, port);
                clearTimeout(timer);
                if (ready) {
                    serviceWrapper.publish(STATE_ONLINE);
                    serviceWrapper.changeStateAndCheck(STATE_ONLINE, true);
                    elog.log("Service [Readiness] [{}] [success]".format(serviceEntity.name));
                    refreshTrayListItemView(serviceEntity, STATE_ONLINE);
                } else {
                    serviceWrapper.changeStateAndCheck(STATE_NOT_READY, true);
                    elog.error("Service [Readiness] [{}] [notReady]".format(serviceEntity.name));
                    if (++count >= MAX_STATE_CHECK_TIME) {
                        elog.error("Service [Readiness] checking has exceeded max checking time [{}] [notReady]".format(serviceEntity.name));
                        refreshTrayListItemView(serviceEntity, STATE_NOT_READY);
                    } else {
                        loopCheck();
                    }
                }
            }, STATE_CHECK_TIME_INTERVAL);
        }

        //delay
        setTimeout(() => {
            loopCheck();
        }, 5 * 1000);
        return;
    }
    serviceWrapper.changeStateAndCheck(STATE_ERRORED, true);
    elog.error("Service[{}] was not started".format(serviceEntity.name));
    refreshTrayListItemView(serviceEntity, STATE_ERRORED);
}

function pm2StartServiceByConfig(configJson) {
    return new Promise((resolve, reject) => {
        elog.info("starting service with config: [{}]".format(JSON.stringify(configJson)));
        pm2.start(configJson, (error, processList) => {
            if (error != null) {
                elog.error("pm2 start service[{}] error:{}".format(configJson.name, error));
                return resolve(false);
            }
            // elog.log(processList);
            for (let proc of processList) {
                elog.log("pm2 service started -->[{}]pid[{}] spawnargs[{}]".format(proc.name, proc.pid, proc.spawnargs))
            }
            return resolve(true)
        });
    });
}

/**
 * 发送通知栏消息
 * @param msg
 */
function notification(msg) {
    console.log(Notification.isSupported())
    // 实例化不会进行通知
    let notification = new Notification({
        // 通知的标题, 将在通知窗口的顶部显示
        title: '服务启动消息',
        // 通知的副标题, 显示在标题下面 macOS
        // subtitle: '重要消息',
        // 通知的正文文本, 将显示在标题或副标题下面
        body: msg,
        // false有声音，true没声音
        silent: false,
        icon: ICON,
        // 通知的超时持续时间 'default' or 'never'
        timeoutType: 'default',
    })
    // 向用户展示 notification
    notification.show();
}

/**
 * 清除serviceWrapper的状态
 */
function resetServiceWrappersState() {
    for (let [serviceName, serviceWrapper] of configLoader.serviceWrapperMap) {
        serviceWrapper.changeStateAndCheck(null, false);
        let dependencyList = serviceWrapper.serviceEntity.depends_on;
        dependencyList && (serviceWrapper.serviceEntity.depends_on_unvValid = JSON.parse(JSON.stringify(dependencyList)));
    }
}